Created: 2024-08-22 Thu 11:25
#pragma once is not standard#define NDEBUG
#include <cassert>
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
struct foo {
#ifndef NDEBUG
int debug_var;
#endif
};
#define private public
#include <complex.h>
template <std::size_t I> struct;
struct foo {
private:
void bar();
public:
void baz();
};
__attribute__((visibility("hidden"))) void foo() {}
std and std.compat modules added in C++23export module foo;
namespace bar {
void baz();
}
foo.bar.barfoo:bar is a module fragment bar inside the module foo, to be covered later
foo defined in a directory bar implemented in a file called baz.cppimport std;
int main() {
std::println("hello");
}
// define the module interface
export module foo;
// import other modules
import foo2;
// internal definitions
void bar() { std::cout << "bar\n"; }
// exported definitions
export void bar();
export { void baz() { quack(); /* from foo2 */ } }
// internal functionality and definitions
module :private;
void bar() { bar(); }
A module unit is a translation unit that contains a module-declaration.
// can only put some things here
module foo;
// this is part of the module
// also a module unit
export module foo;
A module unit purview is the sequence of tokens starting at the module-declaration and extending to the end of the translation unit.
// not module purview
module foo;
// module purview
The global module is the collection of all global-module-fragments and all translation units that are not module units. Declarations appearing in such a context are said to be in the purview of the global module.
A global-module-fragment specifies the contents of the global module fragment for a module unit. The global module fragment can be used to provide declarations that are attached to the global module and usable within the module unit.
module;
// global module fragment
// "Prior to phase 4 of translation, only prepreocessing directives can appear here"
#include <execution>
module foo;
A module interface unit is a module unit whose module-declaration starts with export-keyword; any other module unit is a module implementation unit.
// module interface unit
// not a module implementation unit
export module foo;
// module implementation unit
// not a module interface unit
module foo;
A named module shall contain exactly one module interface unit with no module-partition, known as the primary module interface unit of the module; no diagnostic is required.
// primary module interface unit
export module foo;
// can't have another module interface unit for foo
// export module foo;
A module partition is a module unit whose module-declaration contains a module-partition. A named module shall not contain multiple module partitions with the same module-partition. All module partitions of a module that are module interface units shall be directly or indirectly exported by the primary module interface unit ([module.import]). No diagnostic is required for a violation of these rules.
// module partition
module foo:bar;
// module partition and interface unit
export module foo:baz;
A private-module-fragment shall appear only in a primary module interface unit ([module.unit]). A module unit with a private-module-fragment shall be the only module unit of its module; no diagnostic is required.
module foo;
module :private;
// private module fragment
import std;: https://github.com/pika-org/pika/compare/main...modules-import-std)
pika/
execution/
include/pika/execution/
algo.hppsrc/
algo.cppCMakeLists.txtschedulers/runtime/libpika.sopika.execution etc.pika.all
pika
module.cpp file generated for each pika module, defines module interface// Global module fragment
module;
#include <type_traits>
#include <boost/container/small_vector.hpp>
import std; // If available
// Module interface
export module pika.execution;
// Import other pika modules
import pika.config;
import pika.thread_pools;
// Export everything that we had defined in the headers
export {
#include <pika/execution/algorithms/bulk.hpp>
#include <pika/execution/algorithms/when_all.hpp>
}
#pragma once // still required to avoid multiple definitions in module unit
// Only preprocessor definitions, ok to include; could also be in the global module fragment or command line
#include <pika/config.hpp>
// Keep includes within the "module"
#include <pika/execution/detail/partial_algorithm.hpp>
// Don't include functionality from other pika modules; imported in primary module interface
//#include <pika/functional/tag_invoke.hpp>
// If included in the global module fragment, will not get included again; if using std module don't include
//#include <functional>
//#include <type_traits>
//#include <utility>
// Actual functionality, exported by the export block in the primary module interface
namespace pika::execution {
// ...
}
module;
// Don't include any pika headers here; declared or defined in primary module interface
//#include <pika/execution/detail/helpers.hpp>
//#include <pika/datastructures/variant.hpp>
// #include <pika/string_util/bad_lexical_cast.hpp>
// Functionality used only in the implementation; if using std module don't include, import std instead
#include <typeinfo>
// Module implementation unit
module pika.execution;
// Could import modules for private use here
namespace pika::execution {
// ...
}
PIKA_ASSERT, PIKA_LOG, and PIKA_VERSION etc.inline constexpr variablesstd::source_location and hope for inliningtarget_compile_definitions, but can still pass them manually as compiler flags#include <pika/assertion.hpp>
import pika;
int main() {
PIKA_ASSERT(false);
}
// pika/assertion.hpp
// define macros only
#ifdef PIKA_DEBUG
#define PIKA_ASSERT(...) pika::handle_assertion(...);
#else
#defined PIKA_ASSERT(...)
#endif
// assertion module
module pika.assertion;
export void pika::handle_assertion();
pika.all modulemodule pika.all;
export import pika.assertion;
export import pika.execution;
export import pika.runtime;
// etc.
pika modulemodule pika;
import pika.assertion;
import pika.execution;
import pika.runtime;
// etc.
namespace pika {
export using ::pika::start; // Have to fully qualify names
export using ::pika::stop;
// etc.
}
cmake_minimum_required(VERSION 3.28) # non-experimental in 3.28, import std in 3.30
project(modules CXX) # must declare language
add_library(lib)
target_compile_features(lib PUBLIC cxx_std_20) # at least C++20
target_sources(lib
PUBLIC
FILE_SET cxx_modules TYPE CXX_MODULES # we're building C++ modules
FILES lib.cpp # module unit interfaces
)
target_sources(lib PRIVATE lib_impl.cpp) # module implementation units
modules branch since not all pika modules were translated#include <fmt/printf.hpp>
#include <stdexec/execution.hpp>
#include <pika/assert.hpp>
import std;
import pika;
int main() {
pika::start();
bool result = stdexec::sync_wait(
stdexec::schedule(pika::execution::experimental::thread_pool_scheduler{}) |
stdexec::then([] { fmt::println("hello"); }));
PIKA_ASSERT(result);
pika::finalize();
pika::stop();
}
| variant | libpika/1 thread | libpika/4 threads∗∗ | test executable∗ |
|---|---|---|---|
| modules | 35-45 s (342 targets!) | 20-30 s | 2.5-3.5 s |
| no modules | 115-130 s (72 targets) | 55-70 s | 7-8 s |
| no modules (pch) | 50-60 s | 20-35 s | 3-5 s |
| no modules (pch, unity) | 45-50 s (31 targets) | 25-30 s | 3-5 s |
∗ standalone_thread_pool_scheduler_test
∗∗ benchmarks on noisy 4-core laptop; timing ranges from ~3 builds
| variant | libpika.so | test executable |
|---|---|---|
| modules | 1767584 bytes | 414392 bytes |
| no modules (unity) | 1831248 bytes | 442560 bytes |
target_sources or add_library)CXX_MODULES file setwarning: '#include <filename>' attaches the declarations to the named module 'pika.preprocessor', which is not usually intended; consider moving that directive before module declarationmacro for module mod; module not found#ifdef PIKA_HAVE_MODULE
#define PIKA_MODULE_DECLARATION(name) module name;
#else
#define PIKA_MODULE_DECLARATION(name)
#endif
PIKA_MODULE_DECLARATION(pika.execution)
m are suffixed with @merror: reference to '__and_' is ambiguous
...
note: candidates are: 'template<class ... _Bn> struct std::__and_@pika.config'
...
note: 'template<class ... _Bn> struct std::__and_'
pika/libs/pika/type_support/include/pika/type_support/pack.hpp:17:12: error: cannot declare 'struct pika::util::detail::pack@pika.type_support<Ts>' in a different module
17 | struct pack
| ^~~~
In file included from pika/build/spack/libs/pika/type_support/module.cpp:38,
of module pika.type_support, imported at pika/build/spack/libs/pika/datastructures/module.cpp:26:
pika/libs/pika/type_support/include/pika/type_support/pack.hpp:17:12: note: previously declared here
17 | struct pack
| ^~~~
extern "C++"extern "C++" to not attach a name to the module// fmt.cc
// If you define FMT_ATTACH_TO_GLOBAL_MODULE
// - all declarations are detached from module 'fmt'
// - the module behaves like a traditional static library, too
// - all library symbols are mangled traditionally
// - you can mix TUs with either importing or #including the {fmt} API
#ifdef FMT_ATTACH_TO_GLOBAL_MODULE
extern "C++" {
#endif
staticinline / inline constexprIn file included from /pika/libs/pika/synchronization/src/detail/condition_variable.cpp:26:
include/boost/intrusive/slist.hpp:580:28: error: no matching function for call to 'uncast'
580 | { return const_iterator(detail::uncast(this->get_end_node()), this->priv_value_traits_ptr()); }
| ^~~~~~~~~~~~~~
template<class ConstNodePtr>
static typename uncast_types<ConstNodePtr>::non_const_pointer uncast(const ConstNodePtr & ptr)
export {} blockmodule foo;
export {
#include <bar.hpp>
}
// bar.hpp
static bool bar = false;
bar.hpp: error: declaration of 'bar' with internal linkage cannot be exported
pika/libs/pika/threading/include/pika/threading/thread.hpp:40:23: error: declaration of 'function' must be imported from module 'pika.functional' before it is required
40 | util::detail::function<void(std::exception_ptr const& e)>;
| ^
import std and import std.compat added in C++23
std.compat: The named module std.compat exports the same declarations as the named module std, and additionally exports declarations in the global namespace corresponding to the declarations in namespace std that are provided by the C++ headers for C library facilitiesclang-scan-deps (used by CMake to discover module dependencies)// foo.hpp
template <typename T>
void do_something(T val) {
// ...
}
module;
#include "foo.hpp"
export module acme;
template <typename T>
export void frombulate(T item) {
do_something(item);
}
import acme;
int main() {
frombulate(42); // ERROR: No matching overload of `do_something`!
}
// foo.hpp
template <typename T>
void do_something(T val) {
// ...
}
module;
#include "foo.hpp"
export module acme;
template <typename T>
export void frombulate(T item) {
do_something(item);
}
export void use_it() {
frombulate(true);
}
import acme;
int main() {
frombulate(42); // ERROR: No matching overload of `do_something`!
}